package com.kehutong.crm.message.service;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.coraframework.http.HttpClient;
import org.coraframework.inject.Inject;
import org.coraframework.json.JSONObject;
import org.coraframework.util.CallBack;
import org.coraframework.util.time.TimeUtil;
import com.kehutong.common.urlconfig.SmsUrlConfig;

public class TencentSmsService {

    @Inject
    private SmsUrlConfig smsConfig;

    private final static Charset UTF8 = StandardCharsets.UTF_8;
    private final static String SECRET_ID = "AKIDylITWNIH3WkkX4Haw1pCQQAfwwualARX";
    private final static String SECRET_KEY = "ynMemdF7k66tSqmTuCfrlSrzNbVGMDo4";

    private final static String service = "sms";
    private final static String host = "sms.tencentcloudapi.com";
    private final static String algorithm = "TC3-HMAC-SHA256";

    private static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }

    private static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] d = md.digest(s.getBytes(UTF8));
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }

    private static String getTemplateContent(String templateName, String templateContent, Integer smsType, Integer international, String remark, Integer templateId) {
        StringBuilder buf = new StringBuilder();
        buf.append('{');
        buf.append('"').append("TemplateName").append('"').append(":")
           .append('"').append(templateName).append('"').append(",");
        buf.append('"').append("TemplateContent").append('"').append(":")
           .append('"').append(templateContent).append('"').append(",");
        buf.append('"').append("SmsType").append('"').append(":")
           .append(smsType).append(",");
        buf.append('"').append("International").append('"').append(":")
           .append(international).append(",");

        if (templateId != null)
            buf.append('"').append("TemplateId").append('"').append(":")
               .append(templateId).append(",");

        buf.append('"').append("Remark").append('"').append(":")
           .append('"').append(remark).append('"');
        buf.append('}');
        return buf.toString();
    }

    private static String queryTemplateStatus(List<Integer> templateIdList){
        StringBuilder buf = new StringBuilder();
        buf.append('{');

        buf.append('"').append("TemplateIdSet").append('"').append(":[");
        for (Integer templateId : templateIdList) {
            buf.append(templateId).append(",");
        }
        buf.deleteCharAt(buf.length() - 1);
        buf.append("],");

        buf.append('"').append("International").append('"').append(":")
           .append(0);

        buf.append('}');
        return buf.toString();
    }

    private static String sendContent(List<String> mobiles, int TemplateID, String sign, List<String> randomCodes, String sdkId, String context) {
        StringBuilder buf = new StringBuilder();
        buf.append('{');

        buf.append('"').append("PhoneNumberSet").append('"').append(":[");
        for (String mobile : mobiles) {
            buf.append('"').append("+86").append(mobile).append('"').append(",");
        }
        buf.deleteCharAt(buf.length() - 1);
        buf.append("],");

        buf.append('"').append("TemplateID").append('"').append(":")
           .append('"').append(TemplateID).append('"').append(",");

        buf.append('"').append("Sign").append('"').append(":")
           .append('"').append(sign).append('"').append(",");

        buf.append('"').append("TemplateParamSet").append('"').append(":[");
        for (String code : randomCodes) {
            buf.append('"').append(code).append('"').append(",");
        }
        buf.deleteCharAt(buf.length() - 1);
        buf.append("],");

        buf.append('"').append("SessionContext").append('"').append(":")
           .append('"').append(context).append('"').append(",");

        buf.append('"').append("SmsSdkAppid").append('"').append(":")
           .append('"').append(sdkId).append('"');

        buf.append('}');
        return buf.toString();
    }

    /**
     * 新增模板
     * @param templateName 模板名称
     * @param templateContent 模板内容
     * @param smsType 短信类型，0表示普通短信，1表示营销短信。
     * @param international 是否国际/港澳台短信：0：表示国内短信，1：表示国际/港澳台短信。
     * @param remark 模板备注，例如申请原因，使用场景等。
     * @return
     */
    public JSONObject addTemplate(String templateName, String templateContent, Integer smsType, Integer international, String remark) throws Exception {
        return post(getTemplateContent(templateName, templateContent, smsType, international, remark, null), "AddSmsTemplate");
    }

    /**
     * 修改模板
     * @param templateName 模板名称
     * @param templateContent 模板内容
     * @param smsType 短信类型，0表示普通短信，1表示营销短信。
     * @param international 是否国际/港澳台短信：0：表示国内短信，1：表示国际/港澳台短信。
     * @param remark 模板备注，例如申请原因，使用场景等。
     * @param templateId 模板ID
     * @return
     * @throws Exception
     */
    public JSONObject updateTemplate(String templateName, String templateContent, Integer smsType, Integer international, String remark, Integer templateId) throws Exception {
        return post(getTemplateContent(templateName, templateContent, smsType, international, remark, templateId), "ModifySmsTemplate");
    }

    /**
     * 查询模板状态
     * @param templateIdList 模板ID列表
     * @return
     * @throws Exception
     */
    public JSONObject queryTemplate(List<Integer> templateIdList) throws Exception {
        return post(queryTemplateStatus(templateIdList), "DescribeSmsTemplateList");
    }

    /**
     * 发送短信
     * @param mobiles 手机号列表
     * @param randomCodes 参数列表
     * @param TemplateID 模板ID
     * @return
     * @throws Exception
     */
    public JSONObject sendSms(List<String> mobiles, List<String> randomCodes, int TemplateID, String sign, String context) throws Exception {
        return post(sendContent(mobiles, TemplateID, sign, randomCodes, "1400374763", context), "SendSms");
    }

    /**
     * 待发送内容
     * @param payload 内容
     * @param action 接口名称
     * @return
     * @throws Exception
     */
    public JSONObject post(String payload, String action) throws Exception {
        String timestamp = TimeUtil.timeSec() + "";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 注意时区，否则容易出错
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));

        // ************* 步骤 1：拼接规范请求串 *************
        String httpRequestMethod = "POST";
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json\n" + "host:" + host + "\n";
        String signedHeaders = "content-type;host";

        String hashedRequestPayload = sha256Hex(payload);
        String canonicalRequest =
            httpRequestMethod + '\n' +
                canonicalUri + "\n" +
                canonicalQueryString + "\n" +
                canonicalHeaders + "\n" +
                signedHeaders + "\n" +
                hashedRequestPayload;

        // ************* 步骤 2：拼接待签名字符串 *************
        String credentialScope = date + "/" + service + "/" + "tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;

        // ************* 步骤 3：计算签名 *************
        byte[] secretDate = hmac256(("TC3" + SECRET_KEY).getBytes(UTF8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();

        // ************* 步骤 4：拼接 Authorization *************
        String authorization = algorithm + " " + "Credential=" + SECRET_ID + "/" + credentialScope + ", "
            + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;

        CallBack<HttpClient> httpCallBack = http->{
            TreeMap<String, String> headers = new TreeMap<>();
            headers.put("Authorization", authorization);
            headers.put("Content-Type", "application/json");
            headers.put("Host", host);
            headers.put("X-TC-Action", action);
            headers.put("X-TC-RequestClient", "APIExplorer");
            headers.put("X-TC-Timestamp", timestamp);
            headers.put("X-TC-Version", "2019-07-11");
            headers.put("X-TC-Language", "zh-CN");
            http.setHeaders(headers);
            http.setBody(payload);
        };

        return smsConfig.post("", httpCallBack);
    }
}
